/*
 * Copyright 2008-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.hasor.dataql.fx.python;

import net.hasor.core.AppContext;
import net.hasor.core.spi.SpiTrigger;
import net.hasor.dataql.FragmentProcess;
import net.hasor.dataql.Hints;
import net.hasor.dataql.fx.db.fxquery.DefaultFxQuery;
import net.hasor.dataql.fx.db.fxquery.FxQuery;
import net.hasor.utils.StringUtils;
import org.python.core.*;
import org.python.util.PythonInterpreter;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * 支持 python 的代码片段执行器。
 *
 * @author tdx.liuq
 * @version : 2020-09-26
 */
@Singleton
public class PythonFragment implements FragmentProcess {
    @Inject
    protected AppContext appContext;
    @Inject
    protected SpiTrigger spiTrigger;

    @PostConstruct
    public void init() {

    }

    public List<Object> batchRunFragment(Hints hint, List<Map<String, Object>> params, String fragmentString) throws Throwable {
        if (params == null || params.size() == 0) {
            // 如果批量参数为空退：退化为 非批量
            return Collections.singletonList(this.runFragment(hint, Collections.emptyMap(), fragmentString));
        }
        if (params.size() == 1) {
            // 批量参数只有一组：退化为 非批量
            return Collections.singletonList(this.runFragment(hint, params.get(0), fragmentString));
        }

        List<Object> list = new ArrayList<>();
        for (Map<String, Object> paramMap : params)
            list.add(this.runFragment(hint, paramMap, fragmentString));

        return list;
    }

    /**
     * 分析 Python 代码
     */
    protected FxQuery analysisPython(Hints hint, String fragmentString) {
        return DefaultFxQuery.analysisSQL(fragmentString);
    }

    /**
     * 将Java实例对象转为 PyObject 类型
     *
     * @param value Java实例对象
     * @return PyObject实例对象
     */
    @SuppressWarnings("unchecked")
    protected PyObject parsePyObject(Object value) throws Throwable {
        if (value == null)
            return null;

        if (value instanceof Integer)
            return new PyInteger((Integer) value);
        else if (value instanceof String) {
            return Py.newStringOrUnicode(value.toString());
        } else if (value instanceof Long)
            return new PyLong((Long) value);
        else if (value instanceof Float)
            return new PyFloat((Float) value);
        else if (value instanceof Map) {
            Map<String, Object> newMap;
            try {
                newMap = (Map<String, Object>) value;
            } catch (Exception e) {
                throw new Throwable("转为Map对象异常");
            }
            Map<Object, PyObject> newPyMap = new HashMap<>();
            for (Map.Entry<String, Object> entry : newMap.entrySet())
                newPyMap.put(entry.getKey(), parsePyObject(entry.getValue()));

            return new PyStringMap(newPyMap);
        }

        throw new Throwable("不支持的参数类型:" + value.getClass().getTypeName());
    }

    /**
     * 将 PyObject 转为 Java 对象
     *
     * @param pyObject Py对象
     * @return Java对象
     */
    protected Object parseJavaObject(PyObject pyObject) throws UnsupportedEncodingException {
        if (pyObject == null)
            return null;

        if (pyObject.isInteger())
            return pyObject.asInt();
        else if (pyObject.isNumberType())
            if (pyObject.toString().contains("."))
                return pyObject.asDouble();
            else
                return pyObject.asLong();
        else
            return pyObject.toString();
    }

    @Override
    public Object runFragment(Hints hint, Map<String, Object> paramMap, String fragmentString) throws Throwable {
        // 处理参数
        FxQuery fxQuery = analysisPython(hint, fragmentString);
        String pyCode = fxQuery.buildQueryString(paramMap);

        // 执行 python 代码
        PythonInterpreter interpreter = new PythonInterpreter();
        String keys = StringUtils.join(paramMap.keySet().toArray(), ",");
        Object[] values = paramMap.values().toArray();
        PyObject[] pyValues = new PyObject[values.length];

        for (int i = 0; i < values.length; i++)
            pyValues[i] = parsePyObject(values[i]);

        PyString pyCodeStr = Py.newStringUTF8(
                "# -*- coding: utf-8 -*-\n" +
                        "import sys\n" +
                        "reload(sys)\n" +
                        "sys.setdefaultencoding('utf-8')\n" +
                        "def main(" + keys + "):\n" + pyCode);
        interpreter.exec(pyCodeStr);
        PyFunction pyFunction = interpreter.get("main", PyFunction.class);
        PyObject pyObject = pyFunction.__call__(pyValues);

        interpreter.close();

        return parseJavaObject(pyObject);
    }
}